<template>
  <div class="grid-wrapper">
    <!-- 波浪文本 -->
    <div class="grid-item wave-text">
      <span style="--delay:1">
        所
      </span>
      <span style="--delay:2">
        有
      </span>
      <span style="--delay:3">
        的
      </span>
      <span style="--delay:4">
        美
      </span>
      <span style="--delay:5">
        好
      </span>
      <span style="--delay:6">
        奔
      </span>
      <span style="--delay:7">
        你
      </span>
      <span style="--delay:8">
        而
      </span>
      <span style="--delay:8">
        来
      </span>
    </div>
    <!-- 涟漪效果 -->
    <div class="grid-item ripples">
      <span style="--delay: 1"></span>
      <span style="--delay: 2"></span>
      <span style="--delay: 3"></span>
      <span style="--delay: 4"></span>
      <span style="--delay: 5"></span>
      <span style="--delay: 6"></span>
      <span style="--delay: 7"></span>
      <span style="--delay: 8"></span>
      <span style="--delay: 9"></span>
    </div>
    <div class="grid-item canvas-wrap">
    </div>
    <div class="grid-item video-wrapper">
      four
      <div class="content">
        字幕悬停特效
      </div>
    </div>
    <div class="grid-item canvas-text">
    </div>
    <div class="grid-item canvas-image">
      
    </div>
  </div>
</template>

<script setup>
import { nextTick, onMounted } from 'vue';

const initAnimationCanvas = () => {
  const canvasWrap = document.querySelector('.canvas-wrap');
  canvasWrap.style.backgroundColor = 'rgba(0, 0 , 0 , 0.7)';
  const canvas = document.createElement('canvas');
  canvas.style.width = canvasWrap.offsetWidth;
  canvas.style.height = canvasWrap.offsetHeight;
  canvas.width = canvasWrap.offsetWidth;
  canvas.height = canvasWrap.offsetHeight;
  canvasWrap.appendChild(canvas);
  const ctx = canvas.getContext('2d');
  const g = new Graph(canvas, ctx);
  g.draw();
};

const getRandom = (min, max) => {
  return Math.floor(Math.random() * (max + 1 - min) + min);
};

class Point {
  constructor (canvas) {
    this.r = 6;
    this.canvas = canvas;
    this.x = getRandom(0, canvas.width - this.r / 2) * devicePixelRatio;
    this.y = getRandom(0, canvas.height - this.r / 2) * devicePixelRatio;
    this.xSpeed = getRandom(0, 50);
    this.ySpeed = getRandom(0, 50);
    this.lastDrawTime = null;
  }
  draw (ctx) {
    // 更新坐标
    if(this.lastDrawTime) {
      // 计算新坐标
      const duration = (Date.now() - this.lastDrawTime) / 1000;
      const xDis = this.xSpeed * duration;
      const yDis = this.ySpeed * duration;
      let x = this.x + xDis;
      let y = this.y + yDis;
      if(x > this.canvas.width - this.r / 2) {
        x = this.canvas.width - this.r / 2;
        this.xSpeed = -this.xSpeed;
      }else if(x < 0) {
        x = 0;
        this.xSpeed = -this.xSpeed;
      }

      if(y > this.canvas.height - this.r / 2) {
        y = this.canvas.height - this.r / 2;
        this.ySpeed = - this.ySpeed;
      }else if (y < 0) {
        y = 0;
        this.ySpeed = -this.ySpeed;
      }
      this.x = x;
      this.y = y;
    }
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
    ctx.fillStyle = 'rgb(200, 200, 200)';
    ctx.fill();
    this.lastDrawTime = Date.now();
  }
} 

/** canvas绘制点、线 */
class Graph{
  constructor (canvas, ctx, pointNum = 30, maxDis = 100) {
    this.points = new Array(pointNum).fill(0).map(() => new Point(canvas));
    this.ctx = ctx;
    this.canvas = canvas;
    this.maxDis = maxDis;
  }
  draw () {
    requestAnimationFrame(() => {
      this.draw();
    });
    this.ctx.clearRect(0, 0 , this.canvas.width, this.canvas.height);
    for(let i = 0; i < this.points.length; i ++) {
      const p1 = this.points[i];
      p1.draw(this.ctx);
      for(let j = i + 1; j < this.points.length; j++) {
        
        const p2 = this.points[j];
        const distance = Math.sqrt((p1.x - p2.x) **2 + (p1.y - p2.y) ** 2);
        if(distance > this.maxDis) {
          continue;
        }
        this.ctx.beginPath();
        this.ctx.moveTo(p1.x, p1.y);
        this.ctx.lineTo(p2.x, p2.y);
        this.ctx.lineWidth = 4;
        this.ctx.strokeStyle = `rgba(200, 200, 200, ${1 - distance / this.maxDis})`;
        this.ctx.stroke();
      }

    }
  }
}

/** canvas绘制文字 */
const initCanvasText = () => {
  const textWrap = document.querySelector('.canvas-text');
  textWrap.style.background = '#000';
  const canvas = document.createElement('canvas');
  textWrap.appendChild(canvas);
  canvas.style.width = textWrap.offsetWidth;
  canvas.style.height = textWrap.offsetHeight;
  canvas.width = textWrap.offsetWidth;
  canvas.height = textWrap.offsetHeight;
  const ctx = canvas.getContext('2d');
  const fontSize = 20 * devicePixelRatio;
  const columnCount = Math.floor(canvas.width / fontSize); 
  ctx.font = `${fontSize}px serif`;
  const charIndex = new Array(columnCount).fill(0);
  drawText(canvas, ctx, charIndex, fontSize);
  setInterval(() => drawText(canvas, ctx, charIndex, fontSize), 50);
};

const drawText = (canvas, ctx, charIndex, fontSize) => {
  ctx.fillStyle = 'rgba(0,0,0,0.1)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = '#6E05';
  ctx.textBaseline = 'top';
  for(let i = 0; i < charIndex.length; i++) {
    const x = i * fontSize;
    let y = charIndex[i] * fontSize;
    if(y > canvas.height && Math.random() > 0.98) {
      charIndex[i] = 0;
    }else{
      charIndex[i]++;
    }
    ctx.fillText(randomStr(), x, y);
  }
};

/** 随机字符串 */
const randomStr = () => {
  const str = '0123456789abcdefghijklmnopqwxyz';
  return str[Math.floor(Math.random() * str.length)];
};

/** canvas绘制图像 */
const drawImage = () => {
  const imageWrap = document.querySelector('.canvas-image');
  const canvas = document.createElement('canvas');
  // canvas.style.width = imageWrap.offsetWidth;
  // canvas.style.height = imageWrap.offsetHeight;
  // canvas.width = imageWrap.offsetWidth;
  // canvas.height = imageWrap.offsetHeight;
  imageWrap.appendChild(canvas);
  const ctx = canvas.getContext('2d', { willReadFrequently : true });
  const img = new Image();
  img.onload = (val) => {
    console.log(val);
    canvas.width = img.width;
    canvas.height = img.height;
    // console.log(img.naturalWidth, img.width);
    // img.width = canvas.width / devicePixelRatio;
    // img.height = canvas.height / devicePixelRatio;
    ctx.drawImage(img, 0, 0);
  };
  img.src = new URL('../../assets/cssEffect/girl.jpg', import.meta.url);

  canvas.addEventListener('click', (event) => {
    const x = event.offsetX;
    const y = event.offsetY;
    const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const clickColor = getColor(x, y, imgData);
    // console.log(x, y, color, imgData);

    const greenColor = [0, 255, 0, 255];
    // 递归会造成栈溢出
    // function _changeColor (x, y) {
    //   if(x<0 || x >= canvas.width || y < 0 || y >= canvas.height) {
    //     return;
    //   }

    //   const color = getColor(x, y, imgData);
    //   if(diffColor(clickColor, color) > 100) {
    //     return;
    //   }
    //   if(diffColor(greenColor, color) === 0) {
    //     return;
    //   }
    //   const i = (y * imgData.width + x) * 4;
    //   imgData.data.set(greenColor, i);
    //   _changeColor(x + 1, y);
    //   _changeColor(x - 1, y);
    //   _changeColor(x, y + 1);
    //   _changeColor(x, y - 1);
    // }
    // _changeColor(x, y);
    const stack = [[x, y]];
    const visited = new Set(); // 防止重复
    while(stack.length) {
      const [cx, cy] = stack.pop();
      const key = `${cx},${cy}`;

      if(visited.has(key)) {
        continue;
      }
      visited.add(key);

      const curColor = getColor(cx, cy, imgData);

      if(diffColor(curColor, clickColor) > 100) {
        continue;
      }

      const i = (cy * imgData.width + cx) * 4;
      imgData.data.set(greenColor, i);

      stack.push([cx+1, cy], [cx-1, cy], [cx, cy+1], [cx, cy-1]);
    }
    ctx.putImageData(imgData, 0, 0);
  });
};

const diffColor = (color1, color2) => {
  return Math.abs(color1[0] - color2[0])
    + Math.abs(color1[1] - color2[1])
    +Math.abs(color1[2] - color2[2])
    +Math.abs(color1[3] - color2[3]);
};

const getColor = (x, y, imgData) => {
  const i = (y * imgData.width + x) * 4;
  return [
    imgData.data[i],
    imgData.data[i + 1],
    imgData.data[i + 2],
    imgData.data[i + 3],
  ];
};

onMounted(() => {
  nextTick(() => {
    initAnimationCanvas();
    initCanvasText();
    drawImage();
  });
});
</script>

<style lang="less" scoped>
.grid-wrapper {
    width: calc(100vw - 140px);
    display: grid;
    grid-gap: 10px;
    // 自动换行
    // grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    // 每行固定几个
    grid-template-columns: repeat(3, 1fr);
    // grid-template-rows: 100px;

    .grid-item {
        border: 1px solid var(--grey-border-color);
        background: rgba(0, 0, 0, 0.2);
        &.wave-text {
            background: #000;
            display: flex;
            align-items: center;
            justify-content: center;
            >span {
                top: 50%;
                color: #fff;
                display: inline-block;
                animation: jump 1s ease-in-out infinite;
                animation-delay: calc(0.1s * var(--delay));
            }
            @keyframes jump {
                0% {
                    transform: translateY(0px);
                }
                50% {
                    transform: translateY(-24px);
                }
                40%, 100% {
                    transform: translateY(0px);
                }
            }
        }
        &.ripples {
            position: relative;
            >span {
                position: absolute;
                left: 50%;
                top: 50%;
                transform: translateX(-50%) translateY(-50%);
                display: inline-block;
                background: rgb(70 171 36 / 10%);
                // border: 1px solid var(--grey-border-color);
                animation: enlarge 3s linear infinite;
                animation-delay: calc(0.5s * var(--delay));
                border-radius: 50%;
            }
            @keyframes enlarge {
                0% {
                    width: 0px;
                    height: 0px;
                }
                50% {
                    opacity: 1;
                }
                100% {
                    width: 100px;
                    height: 100px;
                    opacity: 0;
                }
                
            }
        }
        &:nth-child(3) {
            grid-column: 1/3;
            grid-row: 2/4;
        }
        &.video-wrapper {
            position: relative;
            .content {
                position: absolute;
                top: 50%;
                left: -10px;
                background: rgba(0, 0, 0, 0.95);
                color: #fff;
                padding: 20px 60px;
                font-weight: 500;

                transition: transform 0.5s;
                transform-origin: right;
                transform: scaleX(0) translateY(-50%);
            }
            &:hover {
                .content {
                    transition: transform 0.5s;
                    transform-origin: left;
                    transform: scaleX(1) translateY(-50%);
                }
            }
        }
    }
}
</style>